<?php
namespace App\Services\ThirdPayments\Recharge;

use App\Exceptions\RechargeGatewayException;
use App\Lib\Code;
use App\Lib\PaymentMap;
use App\Lib\ThirdPartyMap;
use App\Models\RechargeOrder;
use App\Services\ThirdPayments\BankTraits\HFBBankMap;
use App\Services\ThirdPayments\Contracts\Actions\HrefRedirectAction;
use App\Services\ThirdPayments\Contracts\Actions\QrCodeAction;
use App\Services\ThirdPayments\Contracts\Actions\WapReqAction;
use App\Services\ThirdPayments\Contracts\QRCapable;
use App\Services\ThirdPayments\Contracts\RechargeBaseAbstract;
use App\Services\ThirdPayments\Contracts\WAPable;
use Faker\Provider\Payment;
use GuzzleHttp\Client;
use Jenssegers\Agent\Agent;
use Sunra\PhpSimple\HtmlDomParser;

class HFBPayment extends RechargeBaseAbstract implements QRCapable, WAPable
{
    use HFBBankMap;

    protected $gw_pay = 'https://pay.heepay.com/Payment/Index.aspx';
    protected $gw_query = 'https://query.heepay.com/Payment/Query.aspx';

    public function callback(array $data)
    {
        // TODO: Implement callback() method.
        if ($data['result'] == 1) {
            if ($this->veryCallbackSign($data)) {
                return true;
            }
        }
        return false;
    }

    public function veryCallbackSign(array $data)
    {
        // TODO: Implement veryCallbackSign() method.
        $signStr  = 'result=' . $data['result'] . '&';
        $signStr .= 'agent_id=' . $this->getMchId() . '&';
        $signStr .= 'jnet_bill_no=' . $data['jnet_bill_no'] . '&';
        $signStr .= 'agent_bill_id=' . $data['agent_bill_id'] . '&';
        $signStr .= 'pay_type=' . $data['pay_type'] . '&';
        $signStr .= 'pay_amt=' . $data['pay_amt'] . '&';
        $signStr .= 'remark=' . $data['remark'] . '&';
        $signStr .= 'key=' . $this->getMchKey();
        $sign = md5($signStr);
        if ($sign == $data['sign']) {
            return true;
        }
        return false;
    }

    public function getOrderNoFromCallback(array $data)
    {
        // TODO: Implement getOrderNoFromCallback() method.
        return $data['agent_bill_id'];
    }

    public function getOrderAmtFromCallback(array $data)
    {
        // TODO: Implement getOrderAmtFromCallback() method.
        return $data['pay_amt'];
    }

    public function getThirdNoFromCallback(array $data)
    {
        return $data['jnet_bill_no']; // TODO: Change the autogenerated stub
    }

    /**
     * @return mixed
     */
    public function getIdentify(): string
    {
        // TODO: Implement getIdentify() method.
        return ThirdPartyMap::HUIFUBAO;
    }

    /**
     *  needs child to achieve
     */
    public function initPaymentMap(): array
    {
        // TODO: Implement initPaymentMap() method.
        return [
            PaymentMap::BANK => 20,
            PaymentMap::WX => 30,
            PaymentMap::BANK_WAP => 20,
            PaymentMap::WX_WAP => 30,
            PaymentMap::QQ => 31,
            PaymentMap::QQ_WAP => 31,
        ];
    }

    public function query(RechargeOrder $rechargeOrder)
    {
        // TODO: Implement query() method.
        $this->setParameter('version', 1);
        $this->setParameter('agent_id', $this->getMchId());
        $this->setParameter('agent_bill_id', $rechargeOrder->plat_no);
        $this->setParameter('agent_bill_time', date('YmdHis'));
        $this->setParameter('remark', $this->setRemark($rechargeOrder));
        $this->setParameter('return_mode', 1);
        $this->setParameter('sign', $this->querySign());

        $res = curlHttp($this->getGwQuery(), $this->getParameters());
        if ($res['http_code'] == 200) {
            try {
                $resData = str_replace('|', '&', $res['body']);
                $parseData = parse_url($resData, PHP_URL_QUERY);
                if ($parseData['result'] == 1) {
                    $parseSignStr  = 'agent_id=' . $parseData['agent_id'] . '|';
                    $parseSignStr  .= 'agent_bill_id=' . $parseData['agent_bill_id']. '|';
                    $parseSignStr  .= 'jnet_bill_no=' . $parseData['jnet_bill_no']. '|';
                    $parseSignStr  .= 'pay_type=' . $parseData['pay_type']. '|';
                    $parseSignStr  .= 'result=' . $parseData['agent_id']. '|';
                    $parseSignStr  .= 'pay_amt=' . $parseData['agent_id']. '|';
                    $parseSignStr  .= 'pay_message=' . $parseData['agent_id']. '|';
                    $parseSignStr  .= 'remark=' . $parseData['agent_id']. '|';
                    $parseSignStr  .= 'key=' . $this->getMchKey();
                    if ($parseData['sign'] == md5($parseSignStr)) {
                        return true;
                    } else {
                        return '签名错误';
                    }
                }
            } catch (\Exception $exception) {
                return $exception->getMessage();
            }

        }
        return $res['body'];

    }

    public function bankHref(RechargeOrder $rechargeOrder)
    {
        // TODO: Implement bankHref() method.
        $mchData = $rechargeOrder->order_data;
        $this->setParameter('version', 3);
        $this->setParameter('pay_type', $this->getPaymentMap($mchData['recharge_type']));
        $this->setParameter('pay_code', $this->getBank($mchData['bank_code']));
        $this->setParameter('agent_id', $this->getMchId());
        $agent = new Agent();
        if ($agent->isMobile() || $mchData['recharge_type'] == PaymentMap::BANK_WAP) {
            $this->setParameter('is_phone', 1);
        }
        $this->setParameter('agent_bill_id', $rechargeOrder->plat_no);
        $this->setParameter('bank_card_type', -1);
        $this->setParameter('pay_amt', $rechargeOrder->order_amt);
        $this->setParameter('notify_url', $this->getCallbackUrl());
        $this->setParameter('return_url', $this->getReturnUrl());
        $this->setParameter('user_ip', str_replace('.', '_', $rechargeOrder->req_ip));
        $this->setParameter('agent_bill_time', date('YmdHis'));
        $this->setParameter('goods_name', $mchData['body']);
        $this->setParameter('remark', $this->setRemark($rechargeOrder));
        $this->setParameter('sign',  $this->paySign());

        return $this->getGwPay() . '?' . http_build_query($this->getParameters());
    }

    public function scanCode(RechargeOrder $rechargeOrder)
    {
        // TODO: Implement scanCode() method.
        $mchData = $rechargeOrder->order_data;
        $this->setParameter('version', 1);
        $this->setParameter('pay_type', $this->getPaymentMap($mchData['recharge_type']));
        $this->setParameter('agent_id', $this->getMchId());
        $this->setParameter('agent_bill_id', $rechargeOrder->plat_no);
        $this->setParameter('pay_amt', $rechargeOrder->order_amt);
        $this->setParameter('notify_url', $this->getCallbackUrl());
        $this->setParameter('return_url', $this->getReturnUrl());
        $this->setParameter('user_ip', str_replace('.', '_', $rechargeOrder->req_ip));
        $this->setParameter('agent_bill_time', date('YmdHis'));
        $this->setParameter('goods_name', $mchData['body']);
        $this->setParameter('remark', $this->setRemark($rechargeOrder));
        $this->setParameter('sign', $this->qrCodeSign());
        return $this->getGwPay() . '?' . http_build_query($this->getParameters());
    }

    public function paySign(): string
    {
        // TODO: Implement paySign() method.
        $signStr  = 'version=' . $this->getParameter('version') . '&' ;
        $signStr .= 'agent_id=' . $this->getParameter('agent_id') . '&';
        $signStr .= 'agent_bill_id=' . $this->getParameter('agent_bill_id') . '&';
        $signStr .= 'agent_bill_time=' . $this->getParameter('agent_bill_time') . '&';
        $signStr .= 'pay_type=' . $this->getParameter('pay_type') . '&';
        $signStr .= 'pay_amt=' . $this->getParameter('pay_amt') . '&';
        $signStr .= 'notify_url=' . $this->getParameter('notify_url') . '&';
        $signStr .= 'return_url=' . $this->getParameter('return_url') . '&';
        $signStr .= 'user_ip=' . $this->getParameter('user_ip') . '&';
        $signStr .= 'bank_card_type=' . $this->getParameter('bank_card_type') . '&';
        $signStr .= 'key=' . $this->getMchKey();
        return md5($signStr);
    }

    public function qrCodeSign()
    {
        $signStr  = 'version=' . $this->getParameter('version') . '&' ;
        $signStr .= 'agent_id=' . $this->getParameter('agent_id') . '&';
        $signStr .= 'agent_bill_id=' . $this->getParameter('agent_bill_id') . '&';
        $signStr .= 'agent_bill_time=' . $this->getParameter('agent_bill_time') . '&';
        $signStr .= 'pay_type=' . $this->getParameter('pay_type') . '&';
        $signStr .= 'pay_amt=' . $this->getParameter('pay_amt') . '&';
        $signStr .= 'notify_url=' . $this->getParameter('notify_url') . '&';
        $signStr .= 'return_url=' . $this->getParameter('return_url') . '&';
        $signStr .= 'user_ip=' . $this->getParameter('user_ip') . '&';
        $signStr .= 'key=' . $this->getMchKey();
        return md5($signStr);
    }

    public function querySign(): string
    {
        // TODO: Implement querySign() method.
        $signStr  = 'version=' . $this->getParameter('version') . '&';
        $signStr .= 'agent_id=' . $this->getParameter('agent_id') . '&';
        $signStr .= 'agent_bill_id=' . $this->getParameter('agent_bill_id') . '&';
        $signStr .= 'agent_bill_time=' . $this->getParameter('agent_bill_time') . '&';
        $signStr .= 'return_mode=' . $this->getParameter('return_mode') . '&';
        $signStr .= 'key=' . $this->getMchKey();
        return md5($signStr);
    }

    public function showSuccess(): string
    {
        // TODO: Implement showSuccess() method.
        return 'ok';
    }

    public function initParameters(RechargeOrder $rechargeOrder)
    {
        // TODO: Implement initParameters() method.
    }

    public function wapReq(RechargeOrder $rechargeOrder)
    {
        // TODO: Implement wapReq() method.
        $mchData = $rechargeOrder->order_data;
        $this->setParameter('version', 1);
        $this->setParameter('is_phone', 1);
        $this->setParameter('is_frame', 0);
        $this->setParameter('pay_type', $this->getPaymentMap($mchData['recharge_type']));
        $this->setParameter('agent_id', $this->getMchId());
        $this->setParameter('agent_bill_id', $rechargeOrder->plat_no);
        $this->setParameter('pay_amt', $rechargeOrder->order_amt);
        $this->setParameter('notify_url', $this->getCallbackUrl());
        $this->setParameter('return_url', $this->getReturnUrl());
        $this->setParameter('user_ip', str_replace('.', '_', $rechargeOrder->req_ip));
        $this->setParameter('agent_bill_time', date('YmdHis'));
        $this->setParameter('goods_name', $mchData['body']);
        $this->setParameter('remark', $this->setRemark($rechargeOrder));
        $this->setParameter('sign', $this->qrCodeSign());
        if ($mchData['recharge_type'] == PaymentMap::WX_WAP) {
            $meta_option = [
                's' => 'WAP',
                'n' => config('app.name'),
                'id' => config('app.url')
            ];
            $this->setParameter('meta_option', json_encode($meta_option));
        }
        $url = $this->getGwPay() . '?' . http_build_query($this->getParameters());
        switch ($mchData['recharge_type']) {
            case PaymentMap::WX_WAP:
                $client = new Client();
                $res = $client->request('GET', $url, [
                    'allow_redirects' => [
                        'max'             => 15,        // allow at most 10 redirects.
                        'strict'          => true,      // use "strict" RFC compliant redirects.
                        'referer'         => true,      // add a Referer header
                        'protocols'       => ['http', 'https'], // only allow https URLs
                        'track_redirects' => true
                    ]
                ]);
                $history = $res->getHeader('X-Guzzle-Redirect-History');
                if (count($history) != 0) {
                    return new HrefRedirectAction(end($history));
                } else {
                    throw new RechargeGatewayException($this, $res->getBody(), 'wxWap通道获取错误', Code::RECHARGE_THIRD_LOG);
                }
                break;
            case PaymentMap::QQ_WAP:
                $res = $this->guzzleCurl($url);
                $refer = $res->getHeader('X-Guzzle-Redirect-History');
                if (count($refer) != 0) {
                    $header = end($refer);
                    return new HrefRedirectAction($header);
                } else {
                    throw new RechargeGatewayException($this, $res->getBody(), 'qqWap通道获取错误', Code::RECHARGE_THIRD_LOG);
                }
                break;
            default:
                return new HrefRedirectAction($url);
                break;
        }

        return new HrefRedirectAction($url);
    }

    public function guzzleCurl($url)
    {
        $client = new Client();
        $res = $client->request('GET', $url, [
            'allow_redirects' => [
                'max'             => 15,        // allow at most 10 redirects.
                'strict'          => true,      // use "strict" RFC compliant redirects.
                'referer'         => false,      // add a Referer header
                'protocols'       => ['http', 'https'], // only allow https URLs
                'track_redirects' => true
            ]
        ]);
        return $res;
    }


    public function qrCode(RechargeOrder $rechargeOrder)
    {
        // TODO: Implement qrCode() method.
        $mchData = $rechargeOrder->order_data;
        $url = $this->scanCode($rechargeOrder);
        switch ($mchData['recharge_type']) {
            case PaymentMap::WX:
                $client = new Client();
                $res = $client->request('GET', $url, [
                    'allow_redirects' => [
                        'max'             => 15,        // allow at most 10 redirects.
                        'strict'          => true,      // use "strict" RFC compliant redirects.
                        'referer'         => false,      // add a Referer header
                        'protocols'       => ['http', 'https'], // only allow https URLs
                        'track_redirects' => true
                    ]
                ]);
                $content = $res->getBody();
                $html = HtmlDomParser::str_get_html($content);
                $elem = $html->find('input[id="hidWeiXin"]');
                if (count($elem) != 0) {
                    return new QrCodeAction($elem[0]->getAttribute('value'));
                } else {
                    throw new RechargeGatewayException($this, $content, '扫码通道获取错误', Code::RECHARGE_THIRD_LOG);
                }
                break;
            case PaymentMap::QQ:
                $client = new Client();
                $res = $client->request('GET', $url, [
                    'allow_redirects' => [
                        'max'             => 15,        // allow at most 10 redirects.
                        'strict'          => true,      // use "strict" RFC compliant redirects.
                        'referer'         => false,      // add a Referer header
                        'protocols'       => ['http', 'https'], // only allow https URLs
                        'track_redirects' => true
                    ]
                ]);
                $content = $res->getBody();
                $resData = json_decode($content, true);
                if ($resData['code'] == '0000') {
                    return new QrCodeAction($resData['qr_code_url']);
                } else {
                    throw new RechargeGatewayException($this, $content, 'QQ扫码通道获取错误', Code::RECHARGE_THIRD_LOG);
                }
                break;
            default: break;

        }
    }
}
